import * as vscode from 'vscode';
import { MagicResourceType } from '../types';
import { MirrorMetaManager } from './metaManager';
import { MirrorFileMeta } from './types';

/**
 * 元数据联动维护器
 * 负责在本地对 .meta.json 进行动态维护：
 * - 写入本地操作时间 localUpdateTime（存放于 extra.autoGeneration）
 * - 计算与注入 fullUrlPath（基于 .group-meta.json 和自身 path/requestMapping）
 * - 新增 .ms 文件时生成对应的 .meta.json
 * - .ms 文件重命名/移动时重命名并修正对应的 .meta.json 内容
 * - .ms 文件删除时清理对应的 .meta.json
 * - 分组 .group-meta.json 保存后，批量刷新 fullUrlPath
 */
export class MirrorMetaLinkageManager {
    private metaManager!: MirrorMetaManager;

    public setDependencies(metaManager: MirrorMetaManager) {
        this.metaManager = metaManager;
    }

    private lastSegmentOfPathLike(val?: string): string | undefined {
        if (!val || typeof val !== 'string') return undefined;
        const cleaned = val.trim().replace(/\\/g, '/');
        const noSlash = cleaned.replace(/^\/+/, '').replace(/\/+$/, '');
        if (!noSlash) return undefined;
        const segs = noSlash.split('/');
        return segs[segs.length - 1] || undefined;
    }

    private getExtForType(type: MagicResourceType): string {
        return type === 'datasource' ? '.json' : '.ms';
    }

    private deriveDesiredNameFromMeta(type: MagicResourceType, meta: Partial<MirrorFileMeta>): string | undefined {
        const nameFromMeta = (meta.name || '').trim();
        if (type === 'api') {
            const seg = this.lastSegmentOfPathLike(String((meta as any).requestMapping || (meta as any).path || ''));
            return (seg && seg.length > 0) ? seg : (nameFromMeta || undefined);
        } else if (type === 'function') {
            const seg = this.lastSegmentOfPathLike(String((meta as any).path || ''));
            return (seg && seg.length > 0) ? seg : (nameFromMeta || undefined);
        }
        return nameFromMeta || undefined;
    }

    // 在保存 .meta.json 后，根据 name/path/requestMapping 变化，联动重命名相邻脚本与元数据文件
    public async applyMetaSaveAndSyncNames(
        mirrorRoot: vscode.Uri,
        type: MagicResourceType,
        groupPathSub: string | undefined,
        oldFileName: string,
        incomingMeta: Partial<MirrorFileMeta>
    ): Promise<{ fileName: string; meta: MirrorFileMeta }> {
        const dir = groupPathSub ? `${type}/${groupPathSub}` : `${type}`;
        const ext = this.getExtForType(type);
        const oldMsUri = vscode.Uri.joinPath(mirrorRoot, ...dir.split('/'), `${oldFileName}${ext}`);
        const oldMetaUri = this.metaManager.getMetaFileUriFor(mirrorRoot, type, groupPathSub, oldFileName);

        // 以字段推导目标名称
        const desired = (this.deriveDesiredNameFromMeta(type, incomingMeta) || oldFileName).trim();
        const safeDesired = desired.replace(/[\/:*?"<>|]/g, '_');

        const targetName = safeDesired.length > 0 ? safeDesired : oldFileName;
        const newMsUri = vscode.Uri.joinPath(mirrorRoot, ...dir.split('/'), `${targetName}${ext}`);
        const newMetaUri = this.metaManager.getMetaFileUriFor(mirrorRoot, type, groupPathSub, targetName);

        // 读取现有 meta 作为基底
        const existing = await this.metaManager.readLocalMeta(mirrorRoot, type, groupPathSub, oldFileName);
        const merged: MirrorFileMeta = {
            ...(existing || { name: oldFileName, type, groupPath: dir }),
            ...(incomingMeta || {}),
            name: targetName,
            type,
            groupPath: dir,
        } as MirrorFileMeta;

        // 若目标名称不同，则尝试重命名脚本与 meta 文件
        if (targetName !== oldFileName) {
            try { await vscode.workspace.fs.rename(oldMsUri, newMsUri, { overwrite: true }); } catch(e) {console.warn(e)}
            try { await vscode.workspace.fs.rename(oldMetaUri, newMetaUri, { overwrite: true }); } catch(e) {console.warn(e)}
        }

        // 最终写入 meta（会更新本地时间与 fullUrlPath）
        const touched = this.metaManager.touchLocalMetaTime(merged);
        await this.metaManager.writeLocalMeta(mirrorRoot, touched);
        return { fileName: targetName, meta: touched };
    }
    // 触达一次本地 meta：更新本地时间戳与 fullUrlPath 并写入
    public async touchAndWriteMeta(mirrorRoot: vscode.Uri, meta: MirrorFileMeta): Promise<MirrorFileMeta> {
        const touched = this.metaManager.touchLocalMetaTime(meta);
        await this.metaManager.writeLocalMeta(mirrorRoot, touched);
        return touched;
    }

    // 分组元数据保存：落盘并刷新 fullUrlPath
    public async handleGroupMetaSaved(
        mirrorRoot: vscode.Uri,
        type: MagicResourceType,
        groupPathSub: string | undefined,
        raw: any
    ): Promise<void> {
        await this.metaManager.writeLocalGroupMeta(mirrorRoot, type, groupPathSub, raw);
    }

    // 新建脚本文件时，生成对应的本地 meta（若已存在则跳过）
    public async ensureInitMetaForScript(
        mirrorRoot: vscode.Uri,
        type: MagicResourceType,
        groupPathSub: string | undefined,
        fileName: string,
        opts?: { id?: string }
    ): Promise<MirrorFileMeta> {
        const existing = await this.metaManager.readLocalMeta(mirrorRoot, type, groupPathSub, fileName);
        if (existing) return existing;
        const dir = groupPathSub ? `${type}/${groupPathSub}` : `${type}`;
        // 优先从本地分组元数据读取 groupId 以便新建文件的 meta 自动填充
        let groupId: string | undefined;
        try {
            const gm = await this.metaManager.readLocalGroupMeta(mirrorRoot, type, groupPathSub);
            if (gm && gm.id) groupId = String(gm.id);
        } catch(e) {console.warn(e)}
        const meta: MirrorFileMeta = this.metaManager.touchLocalMetaTime({
            id: opts?.id,
            name: fileName,
            type,
            groupId,
            groupPath: dir,
        } as MirrorFileMeta);
        await this.metaManager.writeLocalMeta(mirrorRoot, meta);
        return meta;
    }

    // 删除脚本文件时，删除对应的本地 meta 文件
    public async deleteMetaForScript(
        mirrorRoot: vscode.Uri,
        type: MagicResourceType,
        groupPathSub: string | undefined,
        fileName: string
    ): Promise<void> {
        const metaUri = this.metaManager.getMetaFileUriFor(mirrorRoot, type, groupPathSub, fileName);
        try { await vscode.workspace.fs.delete(metaUri); } catch(e) {console.warn(e)}
    }

    // 脚本重命名/移动：重命名 meta 文件并修正内容（name/groupPath 保持一致）
    public async renameMetaForScript(
        mirrorRoot: vscode.Uri,
        oldType: MagicResourceType,
        oldGroupPathSub: string | undefined,
        oldFileName: string,
        newType: MagicResourceType,
        newGroupPathSub: string | undefined,
        newFileName: string
    ): Promise<void> {
        const oldMetaUri = this.metaManager.getMetaFileUriFor(mirrorRoot, oldType, oldGroupPathSub, oldFileName);
        const newMetaUri = this.metaManager.getMetaFileUriFor(mirrorRoot, newType, newGroupPathSub, newFileName);
        // 读取旧 meta，如不存在则以默认生成
        const oldMeta = await this.metaManager.readLocalMeta(mirrorRoot, oldType, oldGroupPathSub, oldFileName);
        const newDir = newGroupPathSub ? `${newType}/${newGroupPathSub}` : `${newType}`;
        const next: MirrorFileMeta = this.metaManager.touchLocalMetaTime({
            ...(oldMeta || { name: newFileName, type: newType, groupPath: newDir }),
            name: newFileName,
            type: newType,
            groupPath: newDir,
        } as MirrorFileMeta);
        // 先确保目录再落盘新 meta
        await this.metaManager.writeLocalMeta(mirrorRoot, next);
        // 删除旧 meta（避免残留）
        try { await vscode.workspace.fs.delete(oldMetaUri); } catch(e) {console.warn(e)}
    }

    // 目录重命名/移动：根据新目录位置递归刷新目录下所有 .meta.json 的 groupPath 和 fullUrlPath
    public async refreshGroupPathUnder(
        mirrorRoot: vscode.Uri,
        type: MagicResourceType,
        groupPathSub: string | undefined
    ): Promise<void> {
        const baseDir = vscode.Uri.joinPath(mirrorRoot, type, ...(groupPathSub ? groupPathSub.split('/') : []));
        // 深度优先遍历所有 .meta.json
        const stack: { base: vscode.Uri; segs: string[] }[] = [{ base: baseDir, segs: (groupPathSub ? groupPathSub.split('/').filter(Boolean) : []) }];
        while (stack.length) {
            const cur = stack.pop()!;
            let entries: [string, vscode.FileType][] = [];
            try { entries = await vscode.workspace.fs.readDirectory(cur.base); } catch { entries = []; }
            for (const [name, ft] of entries) {
                if (ft === vscode.FileType.Directory) {
                    stack.push({ base: vscode.Uri.joinPath(cur.base, name), segs: [...cur.segs, name] });
                } else if (ft === vscode.FileType.File && name.endsWith('.meta.json') && name.startsWith('.')) {
                    const fileName = name.replace(/^\./, '').replace(/\.meta\.json$/, '');
                    const sub = cur.segs.join('/');
                    const dir = [type, sub].filter(Boolean).join('/');
                    try {
                        const meta = await this.metaManager.readLocalMeta(mirrorRoot, type, sub, fileName);
                        const next: MirrorFileMeta = this.metaManager.touchLocalMetaTime({
                            ...(meta || { name: fileName, type, groupPath: dir }),
                            name: fileName,
                            type,
                            groupPath: dir,
                        } as MirrorFileMeta);
                        await this.metaManager.writeLocalMeta(mirrorRoot, next);
                    } catch(e) {console.warn(e)}
                }
            }
        }
    }
}


